package com.avantis.admin.config.shiro;

import com.alibaba.fastjson.JSON;
import com.avantis.admin.service.PermissionService;
import com.avantis.common.entity.AdminUser;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class JwtFilter extends BasicHttpAuthenticationFilter {

    public static final String TOKEN_PREFIX = "Bearer ";
    public static final String TOKEN_PREFIX_BASIC = "Basic ";
    public static final String HEADER_SCHEMA = "Authorization";
    public static final String ACCESS_TOKEN = "accessToken";

    private static final Logger LOGGER = LoggerFactory.getLogger(JwtFilter.class);

    @Autowired
    protected StringRedisTemplate writeTemplate;

    public static String getAccessToken(HttpServletRequest httpRequest) {
        String headerToken = httpRequest.getHeader(HEADER_SCHEMA);
        if (!StringUtils.isEmpty(headerToken)) {
            return headerToken;
        }
        String queryToken = httpRequest.getParameter(ACCESS_TOKEN.toLowerCase());
        if (!StringUtils.isEmpty(queryToken)) {
            return queryToken;
        }

        String cookieToken=StringUtils.EMPTY;
        Cookie[] cookies = httpRequest.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (StringUtils.equalsIgnoreCase(ACCESS_TOKEN, cookie.getName())) {
                    cookieToken = cookie.getValue();
                    break;
                }
            }
        }
        return cookieToken;
    }



    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = getAccessToken(httpServletRequest);
        String msg = "";
        Exception e;

//        if(StringUtils.isNotEmpty(httpServletRequest.getParameter("allowAnonymous"))
//        ||StringUtils.isNotEmpty(httpServletRequest.getHeader("allowAnonymous"))){
//            //有allowAnonymous[允许匿名访问]的参数或者头信息,则跳过auth_center认证
//        }
//        else {
//            String sign = httpServletRequest.getHeader("sign");
//            if (StringUtils.isEmpty(sign)) {
//                throw new UnauthenticatedException("auth center 认证失败!");
//            }
//
//            String accessId = httpServletRequest.getHeader("accessId");
//            String accessKey = httpServletRequest.getHeader("accessKey");
//            String timestamp = httpServletRequest.getHeader("timestamp");
//            String systemName = httpServletRequest.getHeader("systemName");
//
//
//            if (StringUtils.isNotEmpty(httpServletRequest.getParameter("allowAnonymous"))) {
//                //有允许匿名访问的参数,则跳过auth_center认证
//            }
//            if (!Md5Util.checkSign(accessId, accessKey, timestamp, systemName, sign)) {
//                throw new UnauthenticatedException("auth center 认证失败!");
//            }
//        }

        //检测到Authorization 需要走jwt filter
        if (StringUtils.isBlank(authorization)) {
            if(hasAnonAccess(request)){
                return true;
            }else {
                msg = "没有令牌，请登录！";
                e = new UnauthenticatedException("没有令牌，请登录！");
                this.response(request, response, e, msg, RespConstants.SC_451);
                return false;
            }
        }else {
            if(!StringUtils.startsWithIgnoreCase(authorization, TOKEN_PREFIX_BASIC.toLowerCase())){
            if (!StringUtils.startsWithIgnoreCase(authorization, TOKEN_PREFIX.toLowerCase())) {
                msg = "令牌必须以bearer 开始，包含空格！";
                e = new UnauthenticatedException("令牌必须以bearer 开始，包含空格！");
                this.response(request, response, e, msg, RespConstants.SC_451);
                return false;
            }
            }
            if (StringUtils.isNotBlank(authorization) && StringUtils.startsWithIgnoreCase(authorization, TOKEN_PREFIX.toLowerCase())) {
                authorization = StringUtils.split(authorization, " ")[1];

                if (!JwtUtils.verify(authorization)) {
                    msg = "令牌已过期！";
                    e = new UnauthenticatedException("令牌已过期！");
                    this.response(request, response, e, msg, RespConstants.SC_452);
                    return false;
                }

                String userString = writeTemplate.opsForValue().get(authorization);
                if (StringUtils.isEmpty(userString)) {
                    msg = "长时间未操作，令牌已被收回！";
                    e = new UnauthenticatedException("长时间未操作，令牌已被收回！");
                    this.response(request, response, e, msg, RespConstants.SC_453);
                    return false;
                }
                return true;
            }
        }
        return false;
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = getAccessToken(httpServletRequest);

        if (StringUtils.isNotBlank(authorization) && StringUtils.startsWithIgnoreCase(authorization, TOKEN_PREFIX.toLowerCase())) {
            authorization = StringUtils.split(authorization, " ")[1];
            AdminUser user = JSON.parseObject(writeTemplate.opsForValue().get(authorization), AdminUser.class);
            CurrentUser.setUser(user);
                JwtToken token = new JwtToken(user.getUsername(), user.getOriginalPassword());
                // 提交给realm进行登入
                getSubject(request, response).login(token);
            //刷新redis的有效期
            writeTemplate.expire(authorization, JwtUtils.REDIS_TOKEN_EXPIRE, TimeUnit.MILLISECONDS);
        }
        return true;
    }


    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginAttempt(request, response)) {
            try {
                return executeLogin(request, response);
            } catch (Exception e) {
                LOGGER.error("filter exception:{0}",e);
                e.printStackTrace();
            }
        }
        return false;
    }


    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

        try {
            /**
             * 对跨域提供支持
             */
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
            httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
            httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
            if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
                httpServletResponse.setStatus(HttpStatus.OK.value());
                return false;
            }
            return super.preHandle(request, response);
        } catch (Exception e) {
            e.printStackTrace();
            HttpServletResponse servletResponse = (HttpServletResponse) response;
            if (e instanceof UnauthenticatedException) {
                servletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            } else if (e instanceof UnauthorizedException) {
                servletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
            } else {
                servletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            }
            servletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);

            Map<String,Object> map=new HashMap<>();
            map.put("timestamp",new Date());
            map.put("status",servletResponse.getStatus());
            if (e instanceof ServletException && e.getCause() != null) {
                map.put("error",e.getCause().getClass().getName());
            }
            else{
                map.put("error",e.getClass().getName());
            }
            map.put("message",e.getMessage());
            map.put("path",(((HttpServletRequest) request).getRequestURI()));
            servletResponse.getWriter().print(JSON.toJSONStringWithDateFormat(map,JSON.DEFFAULT_DATE_FORMAT));
            servletResponse.getWriter().flush();
            return false;
        }
    }


    @Override
    public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
        CurrentUser.setUser(null);
        CurrentUser.remove();
    }

    private void response(ServletRequest request, ServletResponse response,Exception e, String msg,int status) {
        HttpServletResponse servletResponse = (HttpServletResponse) response;
        servletResponse.setStatus(status);
        servletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);

        Map<String,Object> map=new HashMap<>();
        map.put("timestamp",new Date());
        map.put("status",servletResponse.getStatus());
        if (e instanceof ServletException && e.getCause() != null) {
            map.put("error",e.getCause().getClass().getName());
        }
        else{
            map.put("error",e.getClass().getName());
        }
        map.put("message",msg);
        map.put("path",(((HttpServletRequest) request).getRequestURI()));
        try {
            servletResponse.getWriter().print(JSON.toJSONStringWithDateFormat(map,JSON.DEFFAULT_DATE_FORMAT));
            servletResponse.getWriter().flush();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    private boolean hasAnonAccess(ServletRequest request) {
        //拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("druid", "anon");
        filterChainDefinitionMap.put("favicon.png", "anon");//解决弹出favicon.ico下载
        filterChainDefinitionMap.put("logout", "logout");
        filterChainDefinitionMap.put("login", "login");
        filterChainDefinitionMap.put("user", "user");
        filterChainDefinitionMap.put("css", "anon");
        filterChainDefinitionMap.put("swagger-ui.html", "anon");
        filterChainDefinitionMap.put("webjars", "anon");
        filterChainDefinitionMap.put("swagger-resources", "anon");
        filterChainDefinitionMap.put("v2", "anon");
        filterChainDefinitionMap.put("js", "anon");
        filterChainDefinitionMap.put("img", "anon");
        filterChainDefinitionMap.put("font-awesome", "anon");
        filterChainDefinitionMap.put("**", "jwt");
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        if (filterChainDefinitionMap.containsKey(httpServletRequest.getRequestURI().split("/")[1])){
            return true;
        }
        return false;
    }

}
